////////////////////////////////////////////////////////////////////
//                                                                //
//   Source File : codec.cpp                                      //
//   Author : Joe Zheng(Jaredz@163.com)                           //
//   Create : 2010-01-18                                          //
//   Update :                                                     //
//                                                                //
////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "codec.h"
const int LTM_MD5NUM       = 16;
const int LTM_MD5STR       = 36;
//////////////////////////////////////////////////////////////////////////
// CMD5
class CMD5
{
public:
	enum MD5
	{
		MD5_SD11 = 7,
		MD5_SD12 = 12,
		MD5_SD13 = 17,
		MD5_SD14 = 22,
		MD5_SD21 = 5,
		MD5_SD22 = 9,
		MD5_SD23 = 14,
		MD5_SD24 = 20,
		MD5_SD31 = 4,
		MD5_SD32 = 11,
		MD5_SD33 = 16,
		MD5_SD34 = 23,
		MD5_SD41 = 6,
		MD5_SD42 = 10,
		MD5_SD43 = 15,
		MD5_SD44 = 21,

		MD5_STATE = 4,
		MD5_COUNT = 2,
		MD5_BITS  = 32,
		MD5_DATA  = 64,
	};

	typedef struct tagMD5DATA
	{
		DWORD   uCount[MD5_COUNT];
		DWORD   uState[MD5_STATE];
		BYTE    uBuffer[MD5_DATA];
		BYTE    uPadding[MD5_DATA];
	}MD5DATA, *PMD5DATA5;

public:
	CMD5(void)
	: m_bInit(false)
	{
	}

	~CMD5(void)
	{
	}

	void Update(PBYTE pIn, UINT uLen);
	void Final(PBYTE pDstBuf);

private:

	CMD5(const CMD5&)
	: m_bInit(false)
	{
	}

	CMD5& operator=(const CMD5&)
	{
	}

	void Init(void);
	void Transform(DWORD uState[MD5_STATE], BYTE uBlock[MD5_DATA]);
	void Encode(PBYTE  pOut, PDWORD pIn, UINT uLen);
	void Decode(PDWORD pOut, PBYTE  pIn, UINT uLen);

	inline DWORD _F(DWORD x, DWORD y, DWORD z)
	{
		return ((x & y) | ((~x) & z));
	}

	inline DWORD _G(DWORD x, DWORD y, DWORD z)
	{
		return ((x & z) | (y & (~z)));
	}

	inline DWORD _H(DWORD x, DWORD y, DWORD z)
	{
		return (x ^ y ^ z);
	}

	inline DWORD _I(DWORD x, DWORD y, DWORD z)
	{
		return (y ^(x | (~z)));
	}

	inline DWORD _ROTATE_LEFT(DWORD x, DWORD n)
	{
		return ((x << n) | (x >> (MD5_BITS - n)));
	}

	inline void _FF(DWORD& a, DWORD b, DWORD c, DWORD d, DWORD x, DWORD s, DWORD ac)
	{
		a += (_F(b, c, d) + x + ac);
		a  = _ROTATE_LEFT(a, s);
		a += b;
	}

	inline void _GG(DWORD& a, DWORD b, DWORD c, DWORD d, DWORD x, DWORD s, DWORD ac)
	{
		a += (_G(b, c, d) + x + ac);
		a  = _ROTATE_LEFT(a, s);
		a += b;
	}

	inline void _HH(DWORD& a, DWORD b, DWORD c, DWORD d, DWORD x, DWORD s, DWORD ac)
	{
		a += (_H(b, c, d) + x + ac);
		a  = _ROTATE_LEFT(a, s);
		a += b;	
	}

	inline void _II(DWORD& a, DWORD b, DWORD c, DWORD d, DWORD x, DWORD s, DWORD ac)
	{
		a += (_I(b, c, d) + x + ac);
		a  = _ROTATE_LEFT(a, s);
		a += b;
	}

private:
	bool      m_bInit;
	MD5DATA   m_MD5;
};

void CMD5::Update(PBYTE pIn, UINT uLen)
{
	if (m_bInit == false)
	{
		Init();
		m_bInit = true;
	}
	// Compute number of bytes mod 64
	UINT uIndex = (UINT)((m_MD5.uCount[0] >> 3) & 0X3F);
	if ((m_MD5.uCount[0] += (DWORD)(uLen << 3)) < (DWORD)(uLen << 3))
	{
		++m_MD5.uCount[1];
	}

	m_MD5.uCount[1] += (DWORD)(uLen >> 29);
	UINT i        = 0;
	UINT uPartLen = 64 - uIndex;
	if (uLen >= uPartLen)
	{
		memcpy((m_MD5.uBuffer + uIndex), pIn, uPartLen);
		Transform(m_MD5.uState, m_MD5.uBuffer);
		for (i = uPartLen; i + 63 < uLen ; i += 64)
		{
			Transform(m_MD5.uState, (pIn + i));
		}
		uIndex = 0;
	}
	memcpy((m_MD5.uBuffer + uIndex), (pIn + i), (uLen - i));
}

void CMD5::Final(PBYTE pDstBuf)
{
	BYTE bits[8] = { 0 };
	Encode(bits, m_MD5.uCount, 8);
	// Pad out to 56 mod 64
	UINT uIndex  = (UINT)((m_MD5.uCount[0] >> 3) & 0X3F);
	UINT uLength = (uIndex < 56) ? (56 - uIndex) : (120 - uIndex);

	Update(m_MD5.uPadding, uLength);
	Update(bits, 8);
	Encode(pDstBuf, m_MD5.uState, LTM_MD5NUM);
	// Zeroize sensitive information
	memset(&m_MD5, 0, sizeof(MD5)); 
	Init();
}

void CMD5::Init(void)
{
	// http://www.ietf.org/rfc/rfc1321.txt
	m_MD5.uCount[0] = 0;
	m_MD5.uCount[1] = 0;

	m_MD5.uState[0] = 0X67452301;
	m_MD5.uState[1] = 0XEFCDAB89;
	m_MD5.uState[2] = 0X98BADCFE;
	m_MD5.uState[3] = 0X10325476;

	memset(m_MD5.uPadding, 0, sizeof(BYTE) * MD5_DATA);
	m_MD5.uPadding[0] = 0X80;
}

void CMD5::Transform(DWORD uState[MD5_STATE], BYTE uBlock[MD5_DATA])
{
	DWORD a = uState[0];
	DWORD b = uState[1];
	DWORD c = uState[2];
	DWORD d = uState[3];
	DWORD x[16] = { 0 };
	Decode(x, uBlock, MD5_DATA);

	_FF(a, b, c, d, x[ 0], (DWORD)MD5_SD11, (DWORD)0XD76AA478); // 1 
	_FF(d, a, b, c, x[ 1], (DWORD)MD5_SD12, (DWORD)0XE8C7B756); // 2 
	_FF(c, d, a, b, x[ 2], (DWORD)MD5_SD13, (DWORD)0X242070DB); // 3 
	_FF(b, c, d, a, x[ 3], (DWORD)MD5_SD14, (DWORD)0XC1BDCEEE); // 4 
	_FF(a, b, c, d, x[ 4], (DWORD)MD5_SD11, (DWORD)0XF57C0FAF); // 5 
	_FF(d, a, b, c, x[ 5], (DWORD)MD5_SD12, (DWORD)0X4787C62A); // 6 
	_FF(c, d, a, b, x[ 6], (DWORD)MD5_SD13, (DWORD)0XA8304613); // 7 
	_FF(b, c, d, a, x[ 7], (DWORD)MD5_SD14, (DWORD)0XFD469501); // 8 
	_FF(a, b, c, d, x[ 8], (DWORD)MD5_SD11, (DWORD)0X698098D8); // 9 
	_FF(d, a, b, c, x[ 9], (DWORD)MD5_SD12, (DWORD)0X8B44F7AF); // 10
	_FF(c, d, a, b, x[10], (DWORD)MD5_SD13, (DWORD)0XFFFF5BB1); // 11
	_FF(b, c, d, a, x[11], (DWORD)MD5_SD14, (DWORD)0X895CD7BE); // 12
	_FF(a, b, c, d, x[12], (DWORD)MD5_SD11, (DWORD)0X6B901122); // 13
	_FF(d, a, b, c, x[13], (DWORD)MD5_SD12, (DWORD)0XFD987193); // 14
	_FF(c, d, a, b, x[14], (DWORD)MD5_SD13, (DWORD)0XA679438E); // 15
	_FF(b, c, d, a, x[15], (DWORD)MD5_SD14, (DWORD)0X49B40821); // 16

	_GG(a, b, c, d, x[ 1], (DWORD)MD5_SD21, (DWORD)0XF61E2562); // 17 
	_GG(d, a, b, c, x[ 6], (DWORD)MD5_SD22, (DWORD)0XC040B340); // 18 
	_GG(c, d, a, b, x[11], (DWORD)MD5_SD23, (DWORD)0X265E5A51); // 19 
	_GG(b, c, d, a, x[ 0], (DWORD)MD5_SD24, (DWORD)0XE9B6C7AA); // 20 
	_GG(a, b, c, d, x[ 5], (DWORD)MD5_SD21, (DWORD)0XD62F105D); // 21 
	_GG(d, a, b, c, x[10], (DWORD)MD5_SD22, (DWORD)0X02441453); // 22 
	_GG(c, d, a, b, x[15], (DWORD)MD5_SD23, (DWORD)0XD8A1E681); // 23 
	_GG(b, c, d, a, x[ 4], (DWORD)MD5_SD24, (DWORD)0XE7D3FBC8); // 24 
	_GG(a, b, c, d, x[ 9], (DWORD)MD5_SD21, (DWORD)0X21E1CDE6); // 25 
	_GG(d, a, b, c, x[14], (DWORD)MD5_SD22, (DWORD)0XC33707D6); // 26 
	_GG(c, d, a, b, x[ 3], (DWORD)MD5_SD23, (DWORD)0XF4D50D87); // 27 
	_GG(b, c, d, a, x[ 8], (DWORD)MD5_SD24, (DWORD)0X455A14ED); // 28 
	_GG(a, b, c, d, x[13], (DWORD)MD5_SD21, (DWORD)0XA9E3E905); // 29 
	_GG(d, a, b, c, x[ 2], (DWORD)MD5_SD22, (DWORD)0XFCEFA3F8); // 30 
	_GG(c, d, a, b, x[ 7], (DWORD)MD5_SD23, (DWORD)0X676F02D9); // 31 
	_GG(b, c, d, a, x[12], (DWORD)MD5_SD24, (DWORD)0X8D2A4C8A); // 32 

	_HH(a, b, c, d, x[ 5], (DWORD)MD5_SD31, (DWORD)0XFFFA3942); // 33 
	_HH(d, a, b, c, x[ 8], (DWORD)MD5_SD32, (DWORD)0X8771F681); // 34 
	_HH(c, d, a, b, x[11], (DWORD)MD5_SD33, (DWORD)0X6D9D6122); // 35 
	_HH(b, c, d, a, x[14], (DWORD)MD5_SD34, (DWORD)0XFDE5380C); // 36 
	_HH(a, b, c, d, x[ 1], (DWORD)MD5_SD31, (DWORD)0XA4BEEA44); // 37 
	_HH(d, a, b, c, x[ 4], (DWORD)MD5_SD32, (DWORD)0X4BDECFA9); // 38 
	_HH(c, d, a, b, x[ 7], (DWORD)MD5_SD33, (DWORD)0XF6BB4B60); // 39 
	_HH(b, c, d, a, x[10], (DWORD)MD5_SD34, (DWORD)0XBEBFBC70); // 40 
	_HH(a, b, c, d, x[13], (DWORD)MD5_SD31, (DWORD)0X289B7EC6); // 41 
	_HH(d, a, b, c, x[ 0], (DWORD)MD5_SD32, (DWORD)0XEAA127FA); // 42 
	_HH(c, d, a, b, x[ 3], (DWORD)MD5_SD33, (DWORD)0XD4EF3085); // 43 
	_HH(b, c, d, a, x[ 6], (DWORD)MD5_SD34, (DWORD)0X04881D05); // 44 
	_HH(a, b, c, d, x[ 9], (DWORD)MD5_SD31, (DWORD)0XD9D4D039); // 45 
	_HH(d, a, b, c, x[12], (DWORD)MD5_SD32, (DWORD)0XE6DB99E5); // 46 
	_HH(c, d, a, b, x[15], (DWORD)MD5_SD33, (DWORD)0X1FA27CF8); // 47 
	_HH(b, c, d, a, x[ 2], (DWORD)MD5_SD34, (DWORD)0XC4AC5665); // 48 
													 
	_II(a, b, c, d, x[ 0], (DWORD)MD5_SD41, (DWORD)0XF4292244); // 49 
	_II(d, a, b, c, x[ 7], (DWORD)MD5_SD42, (DWORD)0X432AFF97); // 50 
	_II(c, d, a, b, x[14], (DWORD)MD5_SD43, (DWORD)0XAB9423A7); // 51 
	_II(b, c, d, a, x[ 5], (DWORD)MD5_SD44, (DWORD)0XFC93A039); // 52 
	_II(a, b, c, d, x[12], (DWORD)MD5_SD41, (DWORD)0X655B59C3); // 53 
	_II(d, a, b, c, x[ 3], (DWORD)MD5_SD42, (DWORD)0X8F0CCC92); // 54 
	_II(c, d, a, b, x[10], (DWORD)MD5_SD43, (DWORD)0XFFEFF47D); // 55 
	_II(b, c, d, a, x[ 1], (DWORD)MD5_SD44, (DWORD)0X85845DD1); // 56 
	_II(a, b, c, d, x[ 8], (DWORD)MD5_SD41, (DWORD)0X6FA87E4F); // 57 
	_II(d, a, b, c, x[15], (DWORD)MD5_SD42, (DWORD)0XFE2CE6E0); // 58 
	_II(c, d, a, b, x[ 6], (DWORD)MD5_SD43, (DWORD)0XA3014314); // 59 
	_II(b, c, d, a, x[13], (DWORD)MD5_SD44, (DWORD)0X4E0811A1); // 60 
	_II(a, b, c, d, x[ 4], (DWORD)MD5_SD41, (DWORD)0XF7537E82); // 61 
	_II(d, a, b, c, x[11], (DWORD)MD5_SD42, (DWORD)0XBD3AF235); // 62 
	_II(c, d, a, b, x[ 2], (DWORD)MD5_SD43, (DWORD)0X2AD7D2BB); // 63 
	_II(b, c, d, a, x[ 9], (DWORD)MD5_SD44, (DWORD)0XEB86D391); // 64 

	m_MD5.uState[0] += a;
	m_MD5.uState[1] += b;
	m_MD5.uState[2] += c;
	m_MD5.uState[3] += d;
}

void CMD5::Encode(PBYTE  pOut, PDWORD pIn, UINT uLen)
{
	UINT i = 0;
	UINT j = 0;
	for (; j < uLen; ++i, j += 4)
	{
		pOut[j + 0] = (BYTE)(pIn[i] & 0XFF);
		pOut[j + 1] = (BYTE)((pIn[i] >> 8) & 0XFF);
		pOut[j + 2] = (BYTE)((pIn[i] >> 16) & 0XFF);
		pOut[j + 3] = (BYTE)((pIn[i] >> 24) & 0XFF);
	}
}

void CMD5::Decode(PDWORD pOut, PBYTE  pIn, UINT uLen)
{
	UINT i = 0;
	UINT j = 0;
	for (; j < uLen; ++i, j += 4)
	{
		pOut[i] = ((DWORD)pIn[j + 0])       | 
			      ((DWORD)pIn[j + 1] << 8)  | 
				  ((DWORD)pIn[j + 2] << 16) | 
				  ((DWORD)pIn[j + 3] << 24);
	}
}

CMD5 md5instance;

//////////////////////////////////////////////////////////////////////////
// CMD5Codec
CMD5Codec::CMD5Codec(void)
{
}

CMD5Codec::~CMD5Codec(void)
{
}

CMD5Codec::CMD5Codec(const CMD5Codec&)
{
}

CMD5Codec& CMD5Codec::operator=(const CMD5Codec&)
{
	return (*this);
}

int CMD5Codec::Encode(PCTSTR pSrcBuf, PBYTE pDstBuf)
{
	if ((pDstBuf != NULL) && (::IsBadWritePtr(pDstBuf, LTM_MD5NUM) == FALSE))
	{
		int nLen = (int)_tcslen(pSrcBuf);
		md5instance.Update((PBYTE)pSrcBuf, nLen * sizeof(TCHAR));
		md5instance.Final(pDstBuf);
		return nLen;
	}
	return -1;
}

int CMD5Codec::Encode(PCTSTR pSrcBuf, PTSTR pDstBuf)
{
	if ((pDstBuf != NULL) && (::IsBadStringPtr(pDstBuf, LTM_MD5STR) == FALSE))
	{
		BYTE uBuf[LTM_MD5NUM] = { 0 };
		int nLen = (int)_tcslen(pSrcBuf);

		md5instance.Update((PBYTE)pSrcBuf, nLen * sizeof(TCHAR));
		md5instance.Final(uBuf);

		for (int i = 0; i < LTM_MD5NUM; ++i)
		{
			_stprintf((pDstBuf + i * 2), _T("%02x"), uBuf[i]);
		}
		pDstBuf[LTM_MD5STR - 1] = 0;
		return nLen;
	}
	return -1;
}

bool CMD5Codec::Encode(PBYTE pSrcBuf, UINT uSize, PBYTE pDstBuf)
{
	if ((pSrcBuf == NULL) || (pDstBuf == NULL))
	{
		return false;
	}
	if ((::IsBadReadPtr(pSrcBuf, uSize) == FALSE) && (::IsBadWritePtr(pDstBuf, LTM_MD5NUM) == FALSE))
	{
		md5instance.Update(pSrcBuf, uSize);
		md5instance.Final(pDstBuf);
		return true;
	}
	return false;
}

bool CMD5Codec::Encode(PBYTE pSrcBuf, UINT uSize, PTSTR pDstBuf)
{
	if ((pSrcBuf == NULL) || (pDstBuf == NULL))
	{
		return false;
	}
	if ((::IsBadReadPtr(pSrcBuf, uSize) == FALSE) && (::IsBadStringPtr(pDstBuf, LTM_MD5STR) == FALSE))
	{
		BYTE uBuf[LTM_MD5NUM] = { 0 };
		md5instance.Update(pSrcBuf, uSize);
		md5instance.Final(uBuf);

		for (int i = 0; i < LTM_MD5NUM; ++i)
		{
			_stprintf((pDstBuf + i * 2), _T("%02X"), uBuf[i]);
		}
		pDstBuf[LTM_MD5STR - 1] = 0;
		return true;
	}
	return false;
}
